package com.ipan.jfinal.tdo.bigFile;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.ipan.jfinal.tdo.OutputKits;
import com.ipan.jfinal.tdo.config.Mp;
import com.ipan.jfinal.tdo.config.Tpl;
import com.ipan.jfinal.tdo.exception.TransformException;
import com.ipan.jfinal.tdo.out.TdoValidator;
import com.ipan.jfinal.tdo.transform.Transducer;
import com.ipan.kits.base.ExceptionUtil;
import com.ipan.kits.io.FileUtil;
import com.ipan.kits.mapper.ExcelCsvMapper;
import com.ipan.kits.text.Charsets;

/**
 * 
 * 读文件监听器（带模板）
 * 
 * 1）实行严格的转换标准；
 * 2）逐行解析，逐行写入；
 * 3）中途失败，会终止转换，整个转换就失败；
 * 
 * @author iPan
 * @date 2022-01-25
 */
public class ReadBigFileToCsvListener extends AnalysisEventListener<Map<Integer, String>> {
	
	protected Logger logger = LoggerFactory.getLogger(getClass());
	protected Tpl tpl = null;
	protected CSVPrinter printer = null;
	protected File fileOut = null;
	protected Integer rowIndex = -1; // 从0开始
	protected Integer cellIndex = -1; // 从0开始
	/** 数据格式校验 */
	protected TdoValidator validator = TdoValidator.DEFAULT_VALIDATOR;
	protected long startTime = System.currentTimeMillis();
	
	public ReadBigFileToCsvListener(File fout, Tpl tpl) {
		this.tpl = tpl;
		try {
			fileOut = new File(fout.getAbsolutePath() + ".tmp");
			printer = ExcelCsvMapper.me().print(fileOut, Charsets.UTF_8);
		} catch (IOException e) {
			ExceptionUtil.unchecked(e);
		}
	}
	
	public TdoValidator getValidator() {
		return validator;
	}

	public void setValidator(TdoValidator validator) {
		this.validator = validator;
	}

	@Override
	public void invoke(Map<Integer, String> data, AnalysisContext context) {
		// 转换行数据
		try {
			this.rowIndex = context.readRowHolder().getRowIndex(); // 从0计数
			int cfgIndex = this.tpl.getIndex(); // 从0计数
			if (this.rowIndex <= cfgIndex) {
				return ;
			}
			//过滤空行
			if (OutputKits.isBlankLine(data.values())) {
				return ;
			}
			// 校验行
			String errMsg = validator.validate(data, tpl.getKeyType());
			if (StringUtils.isNotBlank(errMsg)) {
				logger.info("文件校验不合格，原因：{}。", errMsg);
				throw new java.lang.IllegalArgumentException(errMsg);
			}
			// 转换行
			Integer rowIndex = context.readRowHolder().getRowIndex(); // 当前行
			Map<Integer, String> retRow = Transducer.me().transform(data, rowIndex, this.tpl);
			// 输出行
			Iterator<Entry<Integer, String>> iter = retRow.entrySet().iterator();
			while (iter.hasNext()) {
				Entry<Integer, String> entry = iter.next();
				String value = entry.getValue();
				cellIndex = entry.getKey();
				printer.print(value);
			}
			printer.println();
		} catch(IOException e) {
			ExceptionUtil.unchecked(e);
		}
	}

	@Override
	public void doAfterAllAnalysed(AnalysisContext context) {
		long second = (System.currentTimeMillis() - startTime) / 1000; // 耗时：秒
		logger.info("转换文件{}完成，耗时{}秒。", context.readWorkbookHolder().getFile().getName(), second);
		if (printer != null) {
			try {
				printer.close();
			} catch (IOException e) {
			}
		}
		// 文件改名
		if (fileOut.exists()) {
			String path = fileOut.getAbsolutePath();
			String reTxtFileName = path.substring(0, path.lastIndexOf("."));
			FileUtil.overrideRenameTo(fileOut, new File(reTxtFileName));
		}
	}
	
	@Override
	public void onException(Exception exception, AnalysisContext context) throws Exception {
		// 记录错误位置
		logger.error("转换文件{}第{}行异常", 
				context.readWorkbookHolder().getFile().getName(), context.readRowHolder().getRowIndex());
		logger.error("文件转换异常", exception);
		// 释放文件资源
		if (printer != null) {
			try {
				printer.close();
			} catch (IOException e) {
			}
		}
		// 抛出转换异常
		if (exception instanceof TransformException) {
			throw exception;
		} else {
			TransformException transformExce = new TransformException(exception.getMessage(), exception);
			transformExce.setRowIndex(rowIndex);
			transformExce.setCellIndex(cellIndex);
			throw transformExce;
		}
	}
	
	@Override
	public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
		this.rowIndex = context.readRowHolder().getRowIndex();
		int currentHeadRowNumber = context.readSheetHolder().getHeadRowNumber();
		// 多行标题的情况，前面几行都会被回调；
		if (this.rowIndex + 1 == currentHeadRowNumber) {
			logger.info("转换文件：{}，{}行。", context.readWorkbookHolder().getFile().getName(), this.rowIndex);
			logger.info("解析标题：{}", headMap);
			// 输出标题信息
			printHead();
		}
	}
	
	// 输出标题信息
	// 自动根据文件类型从配置文件获取转换目标文件的标题
	private void printHead() {
		String fileName = fileOut.getName();
		List<Mp> mappings = tpl.getMappings();
		if (mappings == null || mappings.size() < 1) {
			throw new IllegalArgumentException("文件名" + fileName + "格式不正确或对应的配置不正确！");
		}
		try {
			for (Mp mp : mappings) {
				printer.print(mp.getToLabel());
			}
			printer.println();
		} catch (IOException e) {
			ExceptionUtil.unchecked(e);
		}
	}

}
